module Text.ParserCombinators.Table.TableParser 
       ( parseTable
       , readTable
       , tableParser
       , readTableE
       , parseTableE ) where

import Text.ParserCombinators.Parsec
import Data.Char
import qualified Text.ParserCombinators.Table.CellParser as T
import Control.Monad
import Control.Monad.Error

-- | @tableParser m@ is a parser wich parses ascii table driving by @T.Parser@ monad @m@

tableParser :: T.SParser a -> Parser [a]
tableParser m = columnParser where
  columnParser = do
    x <- rawParser `sepEndBy` eol
    eof
    return $ concat x
  
  rawParser = do { char '#';
                   skipMany $ noneOf "\r\n";
                   return []; }
              <|> do
    skipMany $ oneOf " \t"
    xs <- (quotedField <|> nonquotedField) `sepEndBy` skipMany (oneOf " \t")
    if null xs
      then return []
      else case (parse m "" xs) of
      Left e  -> fail $ show e
      Right x -> return [x] 

quotedField = between (char '"') (char '"') $
              many (noneOf "\"" <|> try (string "\"\"" >> return '"'))

nonquotedField = many1 $ satisfy $ not . isSpace
                 
eol =   try (string "\n\r")
    <|> try (string "\r\n")
    <|> string "\n"
    <|> string "\r"
        
-- | @parseTable s m@ parses ascii table @s@ using monad @m@
--
--
-- Consider the table in the following lines:
--
-- > Body Faces Edges Vertex
-- > Cube         6     12    8
-- > Octahedron   8     12    6
-- > Dodecahedron 12    30    20
-- > Icosahedron  20    30    12
-- 
-- We may parse this table into structure @[(String, Int, Int, Int)]@ in that way
--
-- > import Text.ParserCombinators.Table     
-- > import Text.ParserCombinators.Table.CellParser
-- > import Control.Monad
--
-- > table = "Cube         6     12    8\n" ++
-- >         "Octahedron   8     12    6\n" ++
-- >         "Dodecahedron 12    30    20\n" ++
-- >         "Icosahedron  20    30    12\n"   
--
-- > eulerLaw :: IO ()
-- > eulerLaw = do
-- >   let p = parseTable table $ do         
-- >         b <- string
-- >         f <- int
-- >         e <- int
-- >         v <- int
-- >         return (b,f,e,v)
-- >   case p of
-- >     Left e -> print $ show e
-- >     Right xs -> do
-- >       forM_ xs $ \(b,f,e,v) -> 
-- >         print $ "Body: " ++ b ++ " f+v-e = " ++ show (f+v-e)

parseTable :: [Char] -> T.SParser a -> Either ParseError [a]
parseTable s m = let parser = tableParser m
                 in parse parser "" s

-- | @readTable f m@ reads ascii file @f@ represent table and parses this deriving by monad @m@
-- 

readTable :: FilePath -> T.SParser a -> IO (Either ParseError [a])
readTable f m = do
  s <- readFile f
  return $ parseTable s m

parseTableE :: (Monad m, Error e) => [Char] -> T.SParser a -> ErrorT e m [a]
parseTableE s m = do
  let t = parseTable s m
  case t of 
    Left e  -> throwError $ strMsg $ show e
    Right x -> return x

readTableE :: (Error e) => FilePath -> T.SParser a -> ErrorT e (IO) [a]
readTableE f m = do
  s <- lift $ readFile f
  parseTableE s m
